/**

 * @author admin

 * 2014骞?鏈?鏃?

 */

package com.tmall.testgen.client.runinspring.push.jmx;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.tmall.testgen.client.runinspring.push.web.TestGenMockServletConfig;
import com.tmall.testgen.client.runinspring.push.web.TestGenMockServletContext;
import com.tmall.testgen.client.runinspring.push.web.WebXmlInfo;

public class SpringController {
    private static  Object applicationContext;
    private  static List<Object> servletContextListeners=new ArrayList<Object>();
    private static Object servletContextEventObj;
    private static XStream xstream = new XStream(new DomDriver  ());
    
    public static  void initApplicationContext(String configPath) throws Exception{
        
        if(configPath.endsWith("web.xml")){
            try{
                applicationContext= getApplicationContextFromWebXml(configPath);
            }catch(Exception e){
                throw e;
            }
        }else{
            if(!configPath.startsWith("file:"))
                configPath="file:"+configPath;
        
            Class appContenxtClass=Class.forName("org.springframework.context.support.FileSystemXmlApplicationContext");
            Constructor appContenxtCt=appContenxtClass.getConstructor(String.class);
            applicationContext= appContenxtCt.newInstance(configPath);
        }
    }
    
    public static List<DBInfoForJmx> getDbInfo(){
        return  ConfigExtractor.getDB(applicationContext);
    }

    public static ComputeResult getComputeResult(String clazz,String methodName,Class[] paramTypes ,Object[] params)throws Exception{
        
        
        Class [] paramClazz=null;
        //如果是基本类型，则使用paramTypes
        if(paramTypes!=null){
            paramClazz=new Class[paramTypes.length];
            for(int i=0;i<paramTypes.length;i++){
            if(params[i]==null||isBasicType(params[i].getClass()))
                paramClazz[i]=paramTypes[i];
            else
                paramClazz[i]=Class.forName(paramTypes[i].getName());
            }
        }
        
        GetBeanByClassResult getBeanByClassResult=getBeanByClass(clazz,methodName, paramClazz);
        Map<String,?> beans=getBeanByClassResult.map;
        String beanName=beans.keySet().iterator().next();
        Object bean= beans.get(beanName);
        
        ComputeResult result=new ComputeResult();
        result.setBeanName(beanName);
        
        
        Method method=bean.getClass().getMethod(methodName, paramClazz);
        result.setResult(xstream.toXML(method.invoke(bean, params)));
        result.setBeanClazzName(getBeanByClassResult.clazz.getName());
        result.setBeanClazzSimpleName(getBeanByClassResult.clazz.getSimpleName());
        return result;
    }
    
    public static void clearContext(){
        try{
                    
            for(Object servletContextListenerObj:servletContextListeners){
                Method lm=null;
                try{
                    lm=servletContextListenerObj.getClass().getDeclaredMethod("contextDestroyed", Class.forName("javax.servlet.ServletContextEvent"));
                }catch(Exception e){
                    try{
                    lm=servletContextListenerObj.getClass().getMethod("contextDestroyed", Class.forName("javax.servlet.ServletContextEvent"));
                    }catch(Exception e2){
                        
                    }
                }
                if(lm==null)
                    continue;
                
                try {
                    lm.invoke(servletContextListenerObj, servletContextEventObj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            Method closApplicationContextMethod=applicationContext.getClass().getMethod("destroy");
            closApplicationContextMethod.invoke(applicationContext);
            
            servletContextListeners=new ArrayList<Object>();
            servletContextEventObj=null;
        
    
        }catch(Exception e ){
            
        }
    }
    
    private static class GetBeanByClassResult{
        Class clazz;
        Map<String,?> map;
    }
    
    private static List<Class>getParentInterface(Class clazz){
        List<Class> cis=getParentInterfaceRecursive(clazz);
        java.util.Set<Class> s=new java.util.HashSet();
        for(Class ci:cis){
            s.add(ci);
        }
        
       List<Class> result=new ArrayList();
       for(Class c:s){
           result.add(c);
       }
       return  result;
    }
    private static List<Class> getParentInterfaceRecursive(Class clazz){
        List<Class> result=new ArrayList<Class>();
        if(clazz==null||clazz == Object.class)
            return result;
        
        
        Class[] pis=clazz.getInterfaces();
        Class pcl=clazz.getSuperclass();
        
        if(pis!=null&&pis.length>0){
            for(Class cla:pis) { 
                result.add(cla);
                result.addAll(getParentInterface(cla));
            }
        }
        
        if(pcl != Object.class){
            result.addAll(getParentInterface(pcl));
        }
        
        
        return result;
    }
    
//    public static void main(String[] args) {
//        List l=getParentInterface(LinkedHashMap.class);
//        System.out.println(l);
//    }
    private static  GetBeanByClassResult getBeanByClass(String beanClass,String methodName, Class [] paramClazz)throws Exception{
        
        GetBeanByClassResult result=new GetBeanByClassResult();
        
        Class clazz=Class.forName(beanClass);
        Map<String,Object> beans=new HashMap<String, Object>();
        
        result.clazz=clazz;
        beans.putAll( (Map<String, ?>) applicationContext.getClass().getMethod("getBeansOfType",Class.class).invoke(applicationContext,clazz));
        
        if(beans.size()==0){
            List<Class> classes=getParentInterface(clazz);
            if(classes.size()>0){
                for(Class p:classes){
                    try{
                        Method method=p.getMethod(methodName, paramClazz);
                        if(method!=null){
                            result.clazz=p;
                            beans.putAll( (Map<String, ?>) applicationContext.getClass().getMethod("getBeansOfType",Class.class).invoke(applicationContext,p));
                            break;
                        }
                    }catch(Exception e){
                        
                    }
                    
                }
                
            }
        }
        
        if(beans.size()==0||beans.size()>1){
            Exception e= new Exception("对应的类没有在Spring中注册或存在存在多个注册类型匹配");
            throw e; 
        }
        
        result.map=beans;
        return result;
        
    }
    /**
     * 对函数格式化处理
     */
    private static String formatMethod(String str){
        str=str.trim();
        while(str.indexOf("  ")>0){
            str=str.replaceAll("  ", " ");
        }
        while(str.indexOf(" (")>0){
            str=str.replaceAll(" \\(", "(");
        }
        while(str.indexOf("( ")>0){
            str=str.replaceAll("\\( ", "(");
        }
        while(str.indexOf(" )")>0){
            str=str.replaceAll(" \\)", ")");
        }
        while(str.indexOf(") ")>0){
            str=str.replaceAll("\\) ", ")");
        }
        while(str.indexOf(" ,")>0){
            str=str.replaceAll(" ,", ",");
        }
        while(str.indexOf(", ")>0){
            str=str.replaceAll(", ", ",");
        }
        
        return str;
    }
    
    
    

    /**
     * @param targetProjectWebXMLPath
     * @param classLoader
     * @return
     * @throws Exception
     */
    private static  Object getApplicationContextFromWebXml(String targetProjectWebXMLPath) throws Exception{
        WebXmlInfo webXmlInfo=new WebXmlInfo();
        webXmlInfo.initWebXmlInfo(targetProjectWebXMLPath);
        
        Object testGenMockServletContextObj=Class.forName(TestGenMockServletContext.class.getName()).newInstance();
        Method m=testGenMockServletContextObj.getClass().getDeclaredMethod("setTargetWebAppPath", String.class);
        m.invoke(testGenMockServletContextObj, targetProjectWebXMLPath.toLowerCase().replaceAll("/WEB-INF/web.xml".toLowerCase(), ""));
        
        Method addInitParamM=testGenMockServletContextObj.getClass().getDeclaredMethod("addInitParam", String.class,String.class);
        for(String key:webXmlInfo.getInitParam().keySet()){
            addInitParamM.invoke(testGenMockServletContextObj,key, webXmlInfo.getInitParam().get(key));
        }
        
        
        Constructor servletContextEventCt=Class.forName("javax.servlet.ServletContextEvent").getDeclaredConstructor(Class.forName("javax.servlet.ServletContext"));
        servletContextEventObj= servletContextEventCt.newInstance(testGenMockServletContextObj);
        

        for(String listener:webXmlInfo.getListeners()){
            
            Object servletContextListenerObj =  Class.forName(listener).newInstance();
                Method lm=null;
            try{
                lm=servletContextListenerObj.getClass().getDeclaredMethod("contextInitialized", Class.forName("javax.servlet.ServletContextEvent"));
            }catch(Exception e){
                lm=servletContextListenerObj.getClass().getMethod("contextInitialized", Class.forName("javax.servlet.ServletContextEvent"));
            }
            if(lm==null)
                continue;
            
            servletContextListeners.add(servletContextListenerObj);
            try{
            lm.invoke(servletContextListenerObj, servletContextEventObj);
            }catch(Exception e){
                e.printStackTrace();
            }
            //scl.contextInitialized(event);
            
        }
        

        
        Class ownerClass = Class.forName("org.springframework.web.context.ContextLoader");
        Method getCurrentWebApplicationContextMethod=ownerClass.getMethod("getCurrentWebApplicationContext");
        Object result=getCurrentWebApplicationContextMethod.invoke(ownerClass);
        
        if(result==null&&webXmlInfo.getDispatcherServletClass()!=null){
            Class dispatcherClass=Class.forName(webXmlInfo.getDispatcherServletClass());
            Object  dispatcherObj=dispatcherClass.newInstance();
            
            Object servletConfig=Class.forName(TestGenMockServletConfig.class.getName()).newInstance();
            Method setContextMethod=Class.forName(TestGenMockServletConfig.class.getName()).getDeclaredMethod("setServletContext", Class.forName("javax.servlet.ServletContext"));
            setContextMethod.invoke(servletConfig, testGenMockServletContextObj);
            
            
            
            Method initMethod=Class.forName("javax.servlet.GenericServlet").getDeclaredMethod("init", Class.forName("javax.servlet.ServletConfig"));
            initMethod.invoke(dispatcherObj, servletConfig);
            
            Method getWebApplicationContextMethod=Class.forName("org.springframework.web.servlet.FrameworkServlet").getDeclaredMethod("getWebApplicationContext") ; 
            result=getWebApplicationContextMethod.invoke(dispatcherObj);
        }
        return result;
        
        
    }
    
    private static boolean isBasicType(Class clazz){
        if(clazz==null)
            return true;
        
        if( String.class.isAssignableFrom(clazz) )
            return true;
        
        if(Boolean.class.isAssignableFrom(clazz) ||clazz.getName().equals("boolean"))
            return true;
        if(Byte.class.isAssignableFrom(clazz) ||clazz.getName().equals("byte") )
            return true;
        if(Short.class.isAssignableFrom(clazz) ||clazz.getName().equals("short"))
            return true;
        if(Integer.class.isAssignableFrom(clazz)||clazz.getName().equals("int") )
            return true;
        if(Long.class.isAssignableFrom(clazz)||clazz.getName().equals("long"))
            return true;
        if(Float.class.isAssignableFrom(clazz) ||clazz.getName().equals("float"))
            return true;
        if(Double.class.isAssignableFrom(clazz) ||clazz.getName().equals("double"))
            return true;
        
        
        
        return false;
        
    }

}
